package com.heima.notice.socket;


import com.alibaba.fastjson.JSON;
import com.heima.commons.constant.HtichConstants;
import com.heima.commons.entity.SessionContext;
import com.heima.commons.helper.RedisSessionHelper;
import com.heima.commons.utils.SpringUtil;
import com.heima.modules.vo.NoticeVO;
import com.heima.notice.handler.NoticeHandler;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

//TODO:任务5.1-完成websocket开发-2day
@Component
@ServerEndpoint(value = "/ws/socket")
public class WebSocketServer {//定义了一个名为WebSocketServer的类，用来处理 WebSocket 相关的操作

    //Websocket用户链接池
    //concurrent包的线程安全Map，用来存放每个客户端对应的WebSocketServer对象。
    //key是accountId，可以通过本类中的getAccountId方法获取到，value是session
    public final static Map<String, Session> sessionPools = new ConcurrentHashMap<>();//ConcurrentHashMap：是一种线程安全的 Map（多个人同时操作时不会出问题，比如同时有多个用户上线，不会混乱）。

    /*
        用户发送ws消息，message为json格式{'receiverId':'接收人','tripId':'行程id','message':'消息内容'}
    */
    @OnMessage//WebSocket 的注解，意思是 "当服务器收到客户端发来的消息时，自动执行这个方法"。
    public void onMessage(Session session, String message) {//Session session：当前发消息的客户端的 "连接会话"（知道是谁发来的）；//String message：客户端发来的消息内容（比如 "你好"）。
        String accountId = getAccountId(session);//通过连接会话，获取发消息的用户的accountId（确定 "谁发的"）。
        // 解析消息
        NoticeVO noticeVO = JSON.parseObject(message, NoticeVO.class);//JSON.parseObject(...)自动识别 JSON 字符串里的字段（比如receiverId、message）；找到NoticeVO类中对应的属性；
        noticeVO.setSenderId(accountId);//给消息对象设置 "发送者 ID"（就是上面获取的 accountId，记录 "谁发的"）。
        NoticeHandler noticeHandler = SpringUtil.getBean(NoticeHandler.class);//获取一个专门处理消息的工具（NoticeHandler），这个工具里有保存消息、处理消息的各种功能。
        //设置相关消息内容并存入mongodb：noticeHandler.saveNotice(noticeVO);
        // 保存消息到MongoDB
        noticeHandler.saveNotice(noticeVO);//saveNotice：这是NoticeHandler类中定义的一个方法，专门负责把消息保存到 MongoDB 数据库。//noticeVO：作为参数传给这个方法，里面包含了完整的消息信息（谁发的、发给谁、消息内容等）。

    }


    /**
     * 连接建立成功调用
     *
     * @param session 客户端与socket建立的会话
     * @param session 客户端的userId
     */
    @OnOpen
    public void onOpen(Session session) {
        String accountId = getAccountId(session);//调用getAccountId方法（前面讲过的），通过这个连接（session）拿到连接者的accountId（用户唯一标识，类似用户名）。目的是搞清楚 "谁连进来了"。
        if (accountId != null) {
            sessionPools.put(accountId, session);//如果有效，就把accountId和对应的session存到sessionPools这个 Map 里（前面说过的 "在线用户通讯录"）。
        }
    }


    /**
     * 关闭连接时调用
     *
     * @param session 关闭连接的客户端的姓名
     */
    @OnClose
    public void onClose(Session session) {
        String accountId = getAccountId(session);
        if (accountId != null) {
            sessionPools.remove(accountId);
        }
    }


    /**
     * 发生错误时候
     *
     * @param session
     * @param throwable
     */
    @OnError
    public void onError(Session session, Throwable throwable) {//@OnError：WebSocket 的特殊标记，意思是 "当连接过程中发生错误时，自动执行这个方法"。就像打电话时突然线路出问题了，系统会自动记录这个故障。//Throwable throwable：错误本身的信息（包含错误原因、错误位置等详细内容，相当于 "故障详情单"）。
        System.out.println("发生错误");
        throwable.printStackTrace();//这行是打印错误的详细信息（比如错误类型、在哪行代码出的错、错误原因等），相当于把 "故障详情单" 完整地展示出来。
    }


    /*
     * 在当前session中获取用户accoutId
     */
    private String getAccountId(Session session) {
        String token = null;
        // 获取客户端连接时带来的所有参数（类似网址里的?xxx=xxx&yyy=yyy）
        Map<String, List<String>> paramMap = session.getRequestParameterMap();
        //// 从参数中找到"会话token"对应的参数值（HtichConstants.SESSION_TOKEN_KEY是个常量，比如可能是"token"）
        List<String> paramList = paramMap.get(HtichConstants.SESSION_TOKEN_KEY);
        // 如果找到了这个参数，并且有值，就取出第一个值作为token
        if (paramList != null && paramList.size() != 0) {
            token = paramList.get(0);
        }
        RedisSessionHelper redisSessionHelper = SpringUtil.getBean(RedisSessionHelper.class);//创建一个RedisSessionHelper对象//获取一个叫RedisSessionHelper的工具（专门用来操作 Redis 数据库中的会话信息）。
        if (null == redisSessionHelper) {//如果没拿到这个工具，就返回 null（获取账号 ID 失败）。
            return null;
        }
        SessionContext context = redisSessionHelper.getSession(token);// 用token作为钥匙，从Redis中查到对应的用户会话信息（SessionContext）
        boolean isisValid = redisSessionHelper.isValid(context);// 检查这个用户会话是否有效（比如是否登录过期、是否合法）
        if (isisValid) {// 如果有效，就从会话信息中取出用户的accountId并返回
            return context.getAccountID();
        }
        return null;
    }

}